package lxs.swift.tool;

import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

public class Lambdas {

    private static final Map<String, WeakReference<SerializedLambda>> FUNC_CACHE = new ConcurrentHashMap<>();

    public static <T extends Serializable>  SerializedLambda resolve(T func) {
        Class<?> clazz = func.getClass();
        String name = clazz.getName();
        return FUNC_CACHE.computeIfAbsent(name, (k) -> {
            SerializedLambda lambda = resolveByFn(func);
            return new WeakReference<>(lambda);
        }).get();
    }

    private static <T extends Serializable, R> SerializedLambda resolveByFn(T func) {
        ObjectStreamClass lookup = ObjectStreamClass.lookupAny(func.getClass());
        try {
            Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod");
            writeReplaceMethod.setAccessible(true);
            Method method = (Method) writeReplaceMethod.get(lookup);
            return (SerializedLambda) method.invoke(func);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String toName(SerializedLambda lambda) {
        String implMethodName = lambda.getImplMethodName();
        if (implMethodName.startsWith("get") || implMethodName.startsWith("set")) {
            implMethodName = implMethodName.substring(3);
        }
        if (implMethodName.startsWith("is")) {
            implMethodName = implMethodName.substring(2);
        }
        if (implMethodName.isEmpty()) implMethodName = lambda.getImplMethodName();
        implMethodName = implMethodName.substring(0, 1).toLowerCase() + implMethodName.substring(1);
        return implMethodName;
    }

    public static <T extends Serializable> Class<?>[] getParameterTypes(T lambda) {
        try {
            // 获取 writeReplace 方法
            Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            // 获取 SerializedLambda 实例
            SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(lambda);
            // 获取方法类型字符串
            String methodType = serializedLambda.getInstantiatedMethodType();
            // 解析参数类型
            return parseParameterTypes(methodType);
        } catch (Exception e) {
            throw new RuntimeException("无法提取 Lambda 参数类型", e);
        }
    }

    private static Class<?>[] parseParameterTypes(String methodType) throws ClassNotFoundException {
        // 方法类型格式：(LparamType1;LparamType2;)LreturnType;
        if (methodType == null || !methodType.startsWith("(")) {
            throw new IllegalArgumentException("无效的方法类型字符串");
        }

        // 提取参数部分
        String parameterPart = methodType.substring(1, methodType.indexOf(')'));
        String[] paramDescriptors = parameterPart.split(";");

        Class<?>[] parameterTypes = new Class<?>[paramDescriptors.length];
        for (int i = 0; i < paramDescriptors.length; i++) {
            String descriptor = paramDescriptors[i];
            String className = descriptor.substring(1).replace("/", ".");
            parameterTypes[i] = Class.forName(className);
        }

        return parameterTypes;
    }

    public static <T extends Serializable> Class<?> getReturnType(T lambda) {
        try {
            Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(lambda);
            String methodType = serializedLambda.getInstantiatedMethodType();

            // 提取返回值类型部分
            String returnTypeDescriptor = methodType.substring(methodType.indexOf(')') + 1);
            String returnType = returnTypeDescriptor.replace(")", "").replace(";", ".");
            return Class.forName(returnType);
        } catch (Exception e) {
            throw new RuntimeException("无法提取 Lambda 返回值类型", e);
        }
    }
}
